Google Sign-In for ASP.NET Core Web APIs

There is a lot of tutorials that show how to integrate Google Sign-In in your website, but only a handful show how to integrate it in a REST API.
Today I’ll be showing how you can add Google Sign-In and still manage users in your back-end.

Overview

Here’s a sequence diagram showing how it all works:

Google Sign-In - Sequence Diagram

Google Sign-In - Sequence Diagram

Here are the steps:

  1. The client sends a login request to Google, using the login window (pop-up).
  2. Google responds with an id_token and some user information.
  3. The client sends the id_token to the back-end server.
  4. The back-end server validates the token and gets the user information from it.
  5. Create a user and link the external login to it.
    • If a user already exists with the email address, link directly to him.
    • If the external login is already linked to a user, skip this step.
  6. Send a response to the client.
    • If the response is a success, send a JWT token generated from your back-end for the user to use in all his next requests.

You can also refer to the official guide from Google: Authenticate with a backend server.

Getting the id_token from our client

In order to make it possible for the client to use Google’s login window, we’ll have to use their SDK gapi.

function onSignIn(googleUser) {
  var id_token = googleUser.getAuthResponse().id_token;
  ...
}

If you’re using a framework instead of plain javascript, there are libraries that make this process easier, for instance:

Next, we will send the id_token to our back-end server:

sendGoogleToken (token: string): Observable<GoogleAuthResponse> {
  return this.http.post<GoogleAuthResponse>(this.googleAuthUrl, token)
    .pipe(...);
}

Integrating Google Sign-In in our server

Note: I explain this process using ASP.NET Core but it applies to whatever language/framework you’re using.

Firstly, we’ll have an action (route) to receive the token:

[HttpPost("auth/google")]
[ProducesDefaultResponseType]
public async Task<JsonResult> GoogleLogin(GoogleLoginRequest request)
{
  // ...
}

Secondly, we’ll validate the received token using Google’s Auth API for .NET. To install it, you can use the Package Manager: Install-Package Google.Apis.Auth -Version 1.41.1

Payload payload;
try
{
    payload = await ValidateAsync(request.IdToken, new ValidationSettings
    {
        Audience = new[] { "YOUR_CLIENT_ID" }
    });
    // It is important to add your ClientId as an audience in order to make sure
    // that the token is for your application!
}
catch
{
    // Invalid token
}

Note: If you require users to have confirmed emails, Google’s paypload contains an EmailVerified property that you can check.

Thirdly, we’ll get the user linked to this login attempt (or create it if needed):

public async Task<User> GetOrCreateExternalLoginUser(string provider, string key, string email, string firstName, string lastName)
{
    // Login already linked to a user
    var user = await _userManager.FindByLoginAsync(provider, key);
    if (user != null)
        return user;

    user = await _userManager.FindByEmailAsync(email);
    if (user == null)
    {
      // No user exists with this email address, we create a new one
        user = new User
        {
            Email = email,
            UserName = email,
            FirstName = firstName,
            LastName = lastName
        };

        await _userManager.CreateAsync(user);
    }

    // Link the user to this login
    var info = new UserLoginInfo(provider, key, provider.ToUpperInvariant());
    var result = await _userManager.AddLoginAsync(user, info);
    if (result.Succeeded)
      return user;

    _logger.LogError("Failed add a user linked to a login.");
    _logger.LogError(string.Join(Environment.NewLine, result.Errors.Select(e => e.Description)));
    return null;
}

Lastly, we’ll generate a JWT token:

// How to generate a JWT token is not in this post's scope
public async Task<string> GenerateToken(User user)
{
    var claims = await GetUserClaims(user);
    return GenerateToken(user, claims);
}

Putting everything together gives us:

[HttpPost("auth/google")]
[ProducesDefaultResponseType]
public async Task<JsonResult> GoogleLogin(GoogleLoginRequest request)
{
  Payload payload;
  try
  {
      payload = await ValidateAsync(request.IdToken, new ValidationSettings
      {
          Audience = new[] { "YOUR_CLIENT_ID" }
      });
      // It is important to add your ClientId as an audience in order to make sure
      // that the token is for your application!
  }
  catch
  {
      // Invalid token
  }

  var user = await GetOrCreateExternalLoginUser("google", payload.Subject, payload.Email, payload.GivenName, payload.FamilyName);
  var token = await GenerateToken(user);
  return new JsonResult(token);
  }

I highly recommend putting this in a service or a handler.

Conclusion

In conclusion, integrating Google (or any other provider in that matter) into your sign in/up flow is easier than you think. We saw how to show the user Google’s login window, and, after validating the token in our back-end, link a user entry from our database to that login for future use.
I hope you enjoyed this post!

Zanid Haytam Written by:

Zanid Haytam is an enthusiastic programmer that enjoys coding, reading code, hunting bugs and writing blog posts.

comments powered by Disqus